---
id: entity
title: 9.3 数据库实体
sidebar_label: 9.3 数据库实体
---

## 9.3.1 数据库实体

在面向对象开发思想中，最重要尤为**对象**二字，在 .NET 开发过去，操作数据库往往采用 `DataTable` 和 `DataSet` 来接收数据库返回结果集，而操作数据库也离不开手写 `sql` 语句。

在过去面向过程和应用不发达的时代，这些操作确实好使。然后随着中国互联网网民的激增，电子化时代的到来，各行各业对应用需求也达到了前所未有的量级。

所以，在过去手写 `sql` 的时代各种问题显露无疑：

- 程序员能力参差不齐，写出的 `sql` 性能自然也天差地别
- `sql` 属于字符串硬编程，后期维护难上加难
- 许多单表甚至多表结构一致，出现大量重复 `sql` 代码
- `sql` 本身在不同的数据库提供器中写法有差，后续迁移头痛不已

当然，`sql` 是时代的产物，我们也离不开 `sql`，但对于大多数程序员和项目来说，`sql` 未必能够带给他们多大的效益。

所以，`ORM` 就诞生了，所谓的 `ORM` 就是对象关系映射，英文：`Object Relational Mapping`，简单点说，`ORM` 根据特有的 `POCO 贫血模型` 规则生成 `sql` 语句。大大避免了重复 `sql` 和 `sql` 能力参差不齐等问题。（当然 `ORM` 作者 `sql` 能力也会影响最终性能）

上面所说的 `POCO` 贫血模型正是我们本章节的 **数据库实体**。

简单来说，数据库实体就是数据库表的类表现，通过一定的规则使这个类能够一一对应表结构。通常这样的类也称为：`POCO` 贫血模型，也就是只有定义，没有行为。

## 9.3.2 如何定义实体

`Fur` 框架提供多种定义实体的接口依赖：

- `IEntity`：实体基接口，是所有实体的基接口
- `IEntityNotKey`：无键实体接口，也就是视图、存储过程、函数依赖接口
- `EntityBase`：实体基抽象类，内置了 `Id`，`TenantId` 字段
- `Entity`：实体通用抽象类，继承自 `EntityBase`，同时内置 `CreatedTime`，`UpdatedTime`，`IsDeleted` 字段
- `EntityNotKey`：无键实体抽象类，视图、存储过程、函数依赖抽象类

:::important 实体定义位置

`Fur` 框架中有约定，实体统一定义在 `Fur.Core` 层。

:::

### 9.3.2.1 实体继承选用原则

- 如果你不需要 `Fur` 为实体添加任何内置特性，选用 `IEntity`，无键实体选用 `IEntityNotKey`
- 如果你只需要 `Id` 属性，选用 `EntityBase`
- 如果你需要 `Fur` 为你自动添加常用字段，则选用 `Entity`
- 如果你需要视图、存储过程、函数可以通过 `DbSet` 操作，则继承 `EntityNotKey`

### 9.3.2.2 `IEntity` 示范：

```cs {1,5}
using Fur.DatabaseAccessor;

namespace Fur.Core
{
    public class User : IEntity
    {
        /// <summary>
        /// 手工定义 Id
        /// </summary>
        public int Id { get; set; }

        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }
    }
}
```

### 9.3.2.2 `EntityBase` 示范：

```cs {1,5}
using Fur.DatabaseAccessor;

namespace Fur.Core
{
    public class User : EntityBase
    {
        // 无需定义 Id 属性

        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }
    }
}
```

### 9.3.2.3 `Entity` 示范：

```cs {1,5}
using Fur.DatabaseAccessor;

namespace Fur.Core
{
    public class User : Entity
    {
        // 无需定义 Id 属性
        // 并自动添加 CreatedTime，UpdateTime，IsDeleted 属性

        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }
    }
}
```

### 9.3.2.4 `EntityNotKey` 示范：

```cs {1,5,7-9}
using Fur.DatabaseAccessor;

namespace Fur.Core
{
    public class UserView : EntityNotKey
    {
        public UserView() : base("视图名称")
        {
        }

        /// <summary>
        /// Id
        /// </summary>
        public int Id { get; set; }

        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }
    }
}
```

:::note 特别注意

在 `Fur` 框架中，数据库实体必须直接或间接继承 `IEntity` 才能进行仓储等操作。

:::

## 9.3.3 数据库实体配置

在过去的 `EF Core` 项目开发中，数据库实体配置需要在 `DbContext` 的 `OnModelCreating` 中配置。`Fur` 为了简化配置和提高开发效率，抽象出了 `IEntityTypeBuilder<TEntity>` 接口。

通过 `IEntityTypeBuilder<TEntity>` 接口，我们无需在 `DbContext` 的 `OnModelCreating` 中配置，可在任意地方配置。

### 9.3.3.1 在数据库实体中配置

```cs {1,5,20-25}
using Fur.DatabaseAccessor;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;

namespace Fur.Core
{
    public class User : Entity, IEntityTypeBuilder<User>
    {
        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 年龄
        /// </summary>
        public int Age { get; set; }

        // 配置数据库实体
        public void Configure(EntityTypeBuilder<User> entityBuilder, DbContext dbContext, Type dbContextLocator)
        {
            entityBuilder.HasKey(u => u.Id);
            entityBuilder.HasIndex(u => u.Name);
        }
    }
}
```

### 9.3.3.2 在任何实例类中配置

```cs {1,8,10-14}
using Fur.DatabaseAccessor;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;

namespace Fur.Core
{
    public class SomeClass : IEntityTypeBuilder<User>
    {
        public void Configure(EntityTypeBuilder<User> entityBuilder, DbContext dbContext, Type dbContextLocator)
        {
            entityBuilder.HasKey(u => u.Id);
            entityBuilder.HasIndex(u => u.Name);
        }
    }
}
```

如，上面例子，通过 `SomeClass` 配置 `User` 数据库实体。

## 9.3.4 数据库实体配置说明

`Fur` 框架会自动扫描所有继承 `IEntity` 接口的类进行 `DbSet<TEntity>` 注册，也就是实现自动配置 `DbContext` 的 `OnModelCreating`。

如果需要跳过自动注册，只需要贴 `[NonAutomatic]` 或 `[SkipScan]` 特性即可。一旦贴了此特性，那么就需要手动配置 `DbContext` 的 `OnModelCreating`

## 9.3.5 反馈与建议

:::note 与我们交流

给 Fur 提 [Issue](https://gitee.com/monksoul/Fur/issues/new?issue)。

:::